﻿#define NEW
using System;
using System.Data;
using System.Data.Common;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
using System.Threading;

using RAD.LightOne.Cache;

namespace RAD.LightOne.Data
{
#if !OLD_MODE
    #region Documentation
    /// <summary>
    /// Classe com função de provedor interno de conexão com o banco de dados, baseado no conceito de reaproveitamento
    /// onde uma única conexão pode ser reutilizada n vezes, tal reautilização é controlada internamente e garante melhoria de
    /// performance se comparado à criação de conexões controladas pelo pool de conexões do .NET.
    /// </summary>
    #endregion
    internal static class DBEngineFactory
    {
        #region Fieds

        private static object locker;
        private static DbProviderFactory factory;
        private static ConnectionStringSettings setting;

        private static WeakReference<DBEngine> dbe;
        private static WeakReference<DBEngine> trxDbe;

        #endregion

        #region Properties
        public static DbProviderFactory Factory
        {
            get
            {
                return factory;
            }
        }

        public static ConnectionStringSettings Setting
        {
            get
            {
                return setting;
            }
        }


        #endregion

        #region Contructors

        static DBEngineFactory()
        {
            locker = new object();
            try
            {
                TypesPool.Load();
            }
            catch { }
            try
            {
                string stdConnName = System.Configuration.ConfigurationManager.AppSettings["StdConnection"];
                setting = System.Configuration.ConfigurationManager.ConnectionStrings[stdConnName];
                factory = DbProviderFactories.GetFactory(setting.ProviderName);
                DBEngine.DbProviderFactory = factory;
                DBEngine.Setting = setting;
                Configuration.Initialize(factory);
            }
            catch (ConfigurationErrorsException confExcept)
            {
                throw new ConfigurationErrorsException("The configuration file probably don't well know formated.", confExcept);
            }
            catch (Exception except)
            {
                throw new ConfigurationErrorsException("The app.config or web.config, dont't be configured correctly or connstring don't supplyed by code.", except);
            }
        }

        #region Documentation
        ///<summary>
        /// Construtor privado do dbProviderFactory de DBEngine.
        ///</summary>
        ///<exception cref="ConfigurationErrorsException">Má formação no arquivo de configuração.</exception>
        ///<exception cref="Exception">Demais erros ou falhas de configuração em geral.</exception>
        #endregion

        private static void SetFactoryConfig(ConnectionStringSettings setting)
        {
            try
            {
                factory = DbProviderFactories.GetFactory(setting.ProviderName);
                Configuration.Initialize(factory);
            }
            catch (Exception except)
            {
                throw new ConfigurationErrorsException("The setting supplyed by code is wrong.", except);
            }
        }

        #endregion

        #region Methods
        #region Documentation
        ///<summary>
        /// Retorna uma conexão nova com o banco de dados.
        ///</summary>
        #endregion
        private static DbConnection NewConnection()
        {
            try
            {
                DbConnection connection = factory.CreateConnection();
                connection.ConnectionString = setting.ConnectionString;
                connection.Open();
                return connection;
            }
            catch (Exception ex)
            {
                throw new ConfigurationErrorsException("LightOne unable to open the connection with the passed connection string.", ex);
            }

        }

        #region Documentation
        /// <summary>
        /// Retorna uma conexão aberta com o banco de dados.
        /// </summary>
        #endregion
        internal static DBEngine GetEngine()
        {
            lock (locker)
            {
                DBEngine engine;
#if !NEW
                if (dbe != null && trxDbe == null && dbe.IsAlive && dbe.Target.Connection.State != ConnectionState.Broken)
                {
                    engine = new DBEngine(dbe.Target.InnerConnection);
                }
                else
                {
                    engine = new DBEngine(DBEngineFactory.NewConnection());
                }
#else
                engine = new DBEngine(DBEngineFactory.NewConnection());
#endif
                dbe = new WeakReference<DBEngine>(engine);
                GC.KeepAlive(dbe);
                return engine;
            }
        }

        #region Documentation
        /// <summary>
        /// Retorna uma conexão aberta com o banco de dados.
        /// </summary>
        #endregion
        internal static DBEngine GetEngine(ConnectionStringSettings setting)
        {
            lock (locker)
            {
                DBEngine engine;
#if !NEW
                if (dbe != null && trxDbe == null && dbe.IsAlive && dbe.Target.Connection.State != ConnectionState.Broken)
                {
                    engine = new DBEngine(dbe.Target.InnerConnection);
                }
                else
                {
                    DBEngineFactory.SetFactoryConfig(setting);
                    engine = new DBEngine(DBEngineFactory.NewConnection());
                }
#else
                DBEngineFactory.SetFactoryConfig(setting);
                engine = new DBEngine(DBEngineFactory.NewConnection());
#endif

                dbe = new WeakReference<DBEngine>(engine);
                GC.KeepAlive(dbe);
                return engine;
            }
        }

        internal static DBEngine GetEngine(MyDbTransaction trx)
        {
            lock (locker)
            {
                DBEngine engine = new DBEngine(trx.InnerTransaction);

                trxDbe = new WeakReference<DBEngine>(engine);
                trx.Concluded += new EventHandler(trx_Concluded);
                GC.KeepAlive(engine);
                return engine;
            }
        }

        private static void trx_Concluded(object sender, EventArgs e)
        {
            if (dbe == null)
            {
                dbe = new WeakReference<DBEngine>(trxDbe.Target);  
            }
            trxDbe = null;
        }

        internal static void DefineTransactional(DBEngine dbe)
        {
            if (DBEngineFactory.dbe != null && DBEngineFactory.dbe.IsAlive &&  dbe == DBEngineFactory.dbe.Target)
            {
                DBEngineFactory.dbe = null;
                trxDbe = new WeakReference<DBEngine>(dbe);

                ((MyDbTransaction)trxDbe.Target.Transaction).Concluded += new EventHandler(trx_Concluded);
                GC.KeepAlive(trxDbe);
            }
            else
            {
                trxDbe = new WeakReference<DBEngine>(dbe);
                DBEngineFactory.dbe = trxDbe;
                ((MyDbTransaction)trxDbe.Target.Transaction).Concluded += new EventHandler(trx_Concluded);
                GC.KeepAlive(trxDbe);
            }
        }

        #endregion

    }
#endif
}
